from prj001.serializers import MHistorySerializer, MRelevantSerializer
from prj001.serializers import MSummarySerializer, MCCSerializer
from prj001.serializers import MCureSerializer, MResultsSerializer

from prj001.models.geninfo import GeneralInfo
from myuser.models import MyUser

from prj001.constants import must_be_decimal, pink_chars
from prj001.constants import must_date_type, must_write

from prj001.utils.create import update_model, computed_cure

from django.db import transaction

from .serials import find_today_max

from .doc_of_db import split_doc

import datetime
import re


def date_type(obj, d_type=None):
    if not d_type:
        for key, value in must_write.items():
            if not obj.get(key):
                return '%s - 未填写, 请检查一般信息表' % value

    for k in must_date_type:

        date_str = obj.get(k)

        if not date_str:
            continue

        if re.match(r'\d{4}-\d{2}-\d{2}', date_str):

            t = date_str.split('-')

            if len(t[2]) > 2:
                t_day = t[2].split(' ')[0]
            else:
                t_day = t[2]
            obj[k] = datetime.date(int(t[0]), int(t[1]), int(t_day))
            return None
        else:
            return '日期格式错误'


def define_value(obj, type=None, d_type=None):
    try:
        msg = date_type(obj, d_type=d_type)
    except Exception:
        raise ValueError('请查看填写的时间相关内容, 是否符合 YYYY-MM-DD 样式.')
    obj = {k: v for k, v in obj.items() if v}
    for i, v in obj.items():
        if must_be_decimal.get(i):

            bit_num = int(must_be_decimal.get(i))

            try:
                v = float(v)
            except Exception:
                raise ValueError('需要填入数字类型')

            if bit_num:
                obj[i] = round(v, bit_num)
            else:
                obj[i] = round(v)

            continue

        if type == 'save_excel':
            if pink_chars.get(i):
                if v:
                    obj[i] = 'true'
                else:
                    obj[i] = None
        else:
            if v in ['true', 'false'] and not pink_chars.get(i):
                obj[i] = v.capitalize()

    return (obj, msg)


def save_model(obj, info_obj=None, serializer=None, e_type=None, sf=None, cure_degree=None):
    '''
    obj: 对应数据库表数据的一个字典;
    info_obj: info对象;
    serialer: 对应的序列化, 为了序列obj字典;
    e_type: 表格分析'save_excel'和手机端出入;
    sf: 之前版本手机端没有录入很多表, 兼容文件录入;
    cure_degree: 信息完善度
    '''
    if not obj:
        obj = {}

    info_id = info_obj.id
    try:
        if sf:
            obj.update({'owner': sf.request.user.id})
        else:
            obj.update({'owner': info_obj.owner.id})

        if isinstance(obj, dict):
            try:
                obj, msg = define_value(obj, type=e_type, d_type='obj')
            except ValueError as e:
                raise ValueError(e)

            if msg:
                raise ValueError(msg)
            obj.update({'person': info_id})
        else:
            obj = {'person': info_id}

        try:
            obj_s = serializer(data=obj)
            obj_s.is_valid(raise_exception=True)
            obj_s.save()
        except Exception:
            raise ValueError('保存失败, 请查看数据是否按要求填写.')

        if cure_degree:
            cure_degree = computed_cure(data=obj_s.validated_data)
            info_degree = update_model(info_obj,
                                       tps='object')
            info_obj.degree_of_completion = '%.2f%%' % (
                (cure_degree + info_degree) * 100)
            info_obj.save()

    except ValueError as e:
        raise ValueError(e)

    except Exception:
        raise ValueError('保存失败,请查看对应数据是否符合填写要求.')


def mobile_save_instance(data, e_type=None, sf=None):
    """mobile save instance"""

    info = data.get('info')
    summary = data.get('summary')
    history = data.get('history')
    relevant = data.get('relevant')
    cc = data.get('cc')
    cure = data.get('cure')
    results = data.get('results')

    if not all([info, isinstance(info, dict)]):
        return '请正确填写一般信息'

    if sf:
        info['owner'] = sf.request.user
    info, msg = define_value(info)
    if msg:
        return msg

    info['serial'] = find_today_max()

    owner = info.pop('owner')

    list_of_db = split_doc(GeneralInfo.__doc__)

    if not list_of_db:
        raise ValueError('数据错误')

    del_info = []

    for front_str in info.keys():
        if front_str not in list_of_db:
            del_info.append(front_str)

    if del_info:
        for i in del_info:
            info.pop(i)

    try:
        # owner <==> user_name
        try:
            mobile_user = MyUser.objects.get(id=owner)
        except MyUser.DoesNotExist:
            raise ValueError('对应的账户不存在')

        projects = mobile_user.myprojects.filter(relusers=mobile_user)

        if not projects:
            raise ValueError('该医生不属于此项目')

        this_project = [project for project in projects if 'prj001' in project.linkurl]

        if not this_project:
            raise ValueError('该医生不属于此项目')
    except MyUser.DoesNotExist:
        raise ValueError('账号不存在')
    except ValueError as e:
        raise ValueError(e)

    try:
        with transaction.atomic(savepoint=True):
            point = transaction.savepoint()

            try:
                try:
                    if not sf:
                        # mobile save
                        info.update({'owner_id': mobile_user.id})
                    else:
                        # upload file
                        info.update({'owner_id': sf.request.user.id})
                    degree = update_model(info)
                    # TODO => if mobile API: cut down degree_of_..
                    info_obj = GeneralInfo(degree_of_completion='%.2f%%' % (degree * 100),
                                           **info)
                    info_obj.save()

                except Exception:
                    raise ValueError("基本信息保存失败, 请检查填写内容是否符合要求.")

                save_model(summary,
                           info_obj=info_obj,
                           serializer=MSummarySerializer,
                           e_type=e_type,
                           sf=sf)

                save_model(relevant,
                           info_obj=info_obj,
                           serializer=MRelevantSerializer,
                           e_type=e_type,
                           sf=sf)

                try:
                    save_model(history,
                               info_obj=info_obj,
                               serializer=MHistorySerializer,
                               e_type=e_type,
                               sf=sf)
                except ValueError as e:
                    raise e
                except Exception:
                    raise ValueError('病史信息表保存失败, 请查看数据是否符合填写要求.')
                # if sf => upload file
                # 手机端增加三个表录入

                save_model(cc,
                           info_obj=info_obj,
                           serializer=MCCSerializer,
                           e_type=e_type,
                           sf=sf)

                save_model(cure,
                           info_obj=info_obj,
                           serializer=MCureSerializer,
                           e_type=e_type,
                           sf=sf,
                           cure_degree='cure')

                save_model(results,
                           info_obj=info_obj,
                           serializer=MResultsSerializer,
                           e_type=e_type,
                           sf=sf)

            except ValueError as e:
                transaction.savepoint_rollback(point)
                raise ValueError(e)

            except Exception as e:
                transaction.savepoint_rollback(point)
                raise ValueError(e)
            transaction.savepoint_commit(point)
    except Exception:
        raise ValueError('保存信息失败, 请查看填写的信息是否存在错误.')
    return True
